#region using statements

using System;

using AeonOfStrife.Misc;
using AeonOfStrife.Objects.Castle;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using NGen;
using NGen.Managers.Pathfinding;
using NGen.Systems.Alarms;

#endregion

namespace AeonOfStrife.Objects.Units.UnitStates
{
	public class UnitStateRun : UnitState, IAlarmListener<int>, IPathfindingObject
	{
		private const int c_ALARM_TIME = 1;
		private AlarmObject<int> m_alarm;

		private Vector2 m_velocity;

		private Vector2 m_targetPosition;

		private Vector2 m_currentPathPosition;

		private int m_currentPathIndex;

		private Point[] m_path;
		private Point m_myCell = Point.Zero;

		private PathfindingData m_pathData;

		public UnitStateRun(AnimationInfo p_animationInfo)
			: base(p_animationInfo) {}

		public override void Initialize() {
			Parent.m_spriteEffects = Parent.Target.Position.X < Parent.Position.X
			    ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
			m_pathData = GameEngine.GetService<PathfindingData>();
			m_alarm = new AlarmObject<int>(this, c_ALARM_TIME, 0);
			if(m_myCell == Point.Zero) {
				m_myCell = m_pathData.ConvertScreenToCellCoord(Parent.m_position);
				m_pathData.IncrementMultiplier(m_myCell.X, m_myCell.Y);
			}else {
				m_myCell = m_pathData.ConvertScreenToCellCoord(Parent.m_position);
				UpdatePathfindingData();
			}
		}

		public override void Update(GameTime p_gameTime) {
			Parent.m_spriteEffects = Parent.Target.Position.X < Parent.Position.X
				? SpriteEffects.FlipHorizontally : SpriteEffects.None;

			base.Update(p_gameTime);

			if(m_path == null || m_currentPathIndex == m_path.Length - 1) {
				//find new path to target
				m_targetPosition = Parent.Target.Position;
				GameEngine.GetService<PathfindingData>().Enqueue(this);
				m_velocity = Parent.Target.Position - Parent.m_position;
				m_velocity.Normalize();

			} else {
				//check if next point in path reached
				if(Vector2.DistanceSquared(Parent.m_position, m_currentPathPosition)
				   <= Math.Pow(Parent.MoveSpeedCurrent, 1.2)) {
					if(++m_currentPathIndex >= m_path.Length) {
						m_path = null; //end of path reached
						m_velocity = Vector2.Zero;
						return;
					}
					//get direction to next point
					m_currentPathPosition = m_pathData.ConvertCellCoordToScreenCoord(m_path[m_currentPathIndex]);
					m_velocity = m_currentPathPosition - Parent.m_position;
					m_velocity.Normalize();
				}
			}

			Parent.m_position.X += (float)(m_velocity.X * Parent.MoveSpeedCurrent * p_gameTime.ElapsedGameTime.TotalSeconds);
			Parent.m_position.Y += (float)(m_velocity.Y * Parent.MoveSpeedCurrent * p_gameTime.ElapsedGameTime.TotalSeconds);
			if(Parent.Position.Y <= GameEngine.GameWindow.ClientBounds.Height / 2f + AnimatedSprite.AnimationFrameSize.Y) {
				Parent.m_position.Y = GameEngine.GameWindow.ClientBounds.Height / 2f + AnimatedSprite.AnimationFrameSize.Y;
			} else if(Parent.Position.Y >= GameEngine.GameWindow.ClientBounds.Height) {
				Parent.m_position.Y = GameEngine.GameWindow.ClientBounds.Height-1;
			}
			UpdatePathfindingData();


			if(Parent.InRangeOfTarget()) {
				Parent.Change(Parent.m_attackState);
			}

			m_alarm.Update(p_gameTime);
		}

		public override void Cleanup() {
			base.Cleanup();
			m_path = null;
			m_velocity = Vector2.Zero;
		}

		public override void OnEnter() {
			base.OnEnter();
		}

		public override void OnExit() {
			base.OnExit();
		}

		public void OnAlarmEvent(int p_message) {
			m_alarm.TimeRemaning = GameEngine.Random.Next(c_ALARM_TIME) + 2;
			if(Parent.Target.GetType() == typeof(CastleAttackPoint)) {
				Console.WriteLine("change from castle");
				Parent.Change(Parent.m_idleState);
				return;
			}
			if(m_path == null || m_targetPosition.Equals(Parent.Target.Position)) {
				return;
			}
			m_path = null;
			m_velocity = Vector2.Zero;
		}
		
		internal void UpdatePathfindingData() {
			var current = m_pathData.ConvertScreenToCellCoord(Parent.m_position);
			if(!m_myCell.Equals(current)) {
				m_pathData.DecrementMultiplier(m_myCell.X, m_myCell.Y);
				m_myCell = current;
				m_pathData.IncrementMultiplier(m_myCell.X, m_myCell.Y);
			}
		}

		#region IPathfindingObject implementation

		public void FindPath() {
			if(Parent.Target == null) { 
				return;
				}
			var targetCell = m_pathData.ConvertScreenToCellCoord(Parent.Target.Position);
			m_path = GameEngine.GetService<IManagerPathfinding>().FindPath(m_myCell, targetCell);
			m_velocity = Vector2.Zero;
			if(m_path == null) {
				return;
			}
			m_currentPathIndex = 0;
			m_currentPathPosition = m_pathData.ConvertCellCoordToScreenCoord(m_path[m_currentPathIndex]);
			m_alarm.TimeRemaning = c_ALARM_TIME;

			//get direction to next point
			m_currentPathPosition = m_pathData.ConvertCellCoordToScreenCoord(m_path[m_currentPathIndex]);
			m_velocity = m_currentPathPosition - Parent.m_position;
			m_velocity.Normalize();
		}

		#endregion
	}
}